﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Xml.Linq;
using BulldogCMS.Core.Storage;
using System.Reflection;
using System.IO;
using BulldogCMS.Core.Storage.Providers;
using BulldogCMS.Core.Runtime.Modules;
using BulldogCMS.Core.MVC;
using BulldogCMS.Core.Storage.Cache;

namespace BulldogCMS.Core.Runtime
{
    public class ConfigurationLoader
    {
        public IEnumerable<Assembly> Assemblies
        {
            get;
            private set;
        }

        private XDocument _configurationDoc = null;
        private XDocument _treeDoc = null;

        public ConfigurationLoader(XDocument configurationDoc, XDocument treeDoc)
        {
            this._configurationDoc =    configurationDoc;
            this._treeDoc =             treeDoc;
        }

        // <site hostName="local.sejcek.cz" language="cs-CZ" contentPath="/Content/ComputerShop" />
        public IEnumerable<Site> LoadSites()
        {
            List<Site> sites = new List<Site>();
            XElement sitesEl = _configurationDoc.Root.Element("sites");
            if (sitesEl != null)
            {
                foreach (XElement siteEl in sitesEl.Elements())
                {
                    Site site = new Site()
                    {
                        HostName = siteEl.GetAttributeValue<string>("hostName"),
                        Language = siteEl.GetAttributeValue<string>("language"),
                        ContentPath = siteEl.GetAttributeValue<string>("contentPath"),
                        Port = siteEl.GetAttributeValue<int>("port"),
                        DefaultNodeVersion = siteEl.GetAttributeValue<SiteDefaultNodeVersion>("defaultNodeVersion")
                    };

                    sites.Add(site);
                }
            }
            return sites;
        }

        public IEnumerable<Language> LoadLanguages()
        {
            List<Language> languages = new List<Language>();
            XElement languagesEl = _configurationDoc.Root.Element("languages");
            if (languagesEl != null)
            {
                foreach (XElement languageEl in languagesEl.Elements())
                {
                    Language lang = new Language()
                    {
                        Name = languageEl.GetAttributeValue<string>("name")
                    };
                    languages.Add(lang);
                }
            }
            return languages;
        }

        public IEnumerable<Assembly> LoadAssemblies()
        {
            XElement assembliesEl = _configurationDoc.Root.Element("assemblies");
            List<Assembly> assemblies = new List<Assembly>();
            if (assembliesEl != null)
            {
                foreach (XElement assemblyEl in assembliesEl.Elements())
                {
                    string type = assemblyEl.Attribute("type").Value;
                    Assembly assembly = Assembly.Load(type);
                    assemblies.Add(assembly);
                }
            }
            this.Assemblies = assemblies;
            return assemblies;
        }

        public IEnumerable<Templates.TemplateMeta> LoadTemplates()
        {
            List<Templates.TemplateMeta> templates = new List<Templates.TemplateMeta>();
            foreach (Assembly assembly in this.Assemblies)
            {
                string assemblyShortName = assembly.GetName().Name;
                foreach (string resourceName in assembly.GetManifestResourceNames())
                {
                    if (resourceName.StartsWith(assemblyShortName + ".Templates") && resourceName.EndsWith(".xml"))
                    {
                        Stream templateStream = assembly.GetManifestResourceStream(resourceName);
                        if (templateStream != null)
                        {
                            XDocument templateDoc = XDocument.Load(templateStream);
                            Templates.TemplateMeta template = new Templates.TemplateMeta(resourceName.CutEnd(".xml"), templateDoc);
                            templates.Add(template);
                        }
                    }
                }
            }
            return templates;
        }

        public IEnumerable<Storage.DataTypeMeta> LoadDataTypes()
        {
            List<Storage.DataTypeMeta> dataTypeMetas = new List<Storage.DataTypeMeta>();
            Type baseType = typeof(BulldogCMS.Core.Storage.DataTypes.BaseDataType);
            foreach (Assembly assembly in this.Assemblies)
            {
                foreach (var type in assembly.GetExportedTypes())
                {
                    if (type.BaseType == baseType)
                    {
                        Storage.DataTypeMeta dataTypeMeta = new DataTypeMeta()
                        {
                            Name = type.FullName,
                            Type = type
                        };
                        dataTypeMetas.Add(dataTypeMeta);
                    }
                }
            }
            return dataTypeMetas;
        }

        public IUniqueIdentifierProvider LoadUniqueIdentifierProvider()
        {
            XElement idGeneratorEl = _configurationDoc.Root.Element("uniqueIdentifierGenerator");
            if (idGeneratorEl != null)
            {
                string providerType = idGeneratorEl.GetAttributeValue<string>("providerType");
                if (providerType != null)
                {
                    IUniqueIdentifierProvider idProvider = providerType.Instantiate<IUniqueIdentifierProvider>();
                    if (idProvider != null)
                    {
                        // parameters
                        if (idGeneratorEl.Element("parameters") != null)
                        {
                            foreach (XElement parEl in idGeneratorEl.Element("parameters").Elements("parameter"))
                            {
                                idProvider.SetParameter(parEl.GetAttributeValue<string>("name"), parEl.GetAttributeValue<string>("value"));
                            }
                        }
                        return idProvider;
                    }
                }
            }
            throw new ApplicationException("UniqueIdentifierGenerator is not set in configuration or providerType attribute does not contain proper type.");
        }

        public IRequestContextProvider LoadRequestContextProvider()
        {
            XElement requestContextProviderEl = _configurationDoc.Root.Element("requestContextProvider");
            if (requestContextProviderEl != null)
            {
                string providerType = requestContextProviderEl.GetAttributeValue<string>("providerType");
                if (providerType != null)
                {
                    IRequestContextProvider requestContextProvider = providerType.Instantiate<IRequestContextProvider>();
                    if (requestContextProvider != null)
                    {
                        return requestContextProvider;
                    }
                }
            }
            throw new ApplicationException("RequestProvider is not set in configuration or providerType attribute does not contain proper type.");
        }

        public IStorageCache LoadStorageCacheProvider()
        {
            XElement storageCacheProviderEl = _configurationDoc.Root.Element("storageCache");
            if (storageCacheProviderEl != null)
            {
                string providerType = storageCacheProviderEl.GetAttributeValue<string>("providerType");
                if (providerType != null)
                {
                    IStorageCache storageCacheProvider = providerType.Instantiate<IStorageCache>();
                    if (storageCacheProvider != null)
                    {
                        return storageCacheProvider;
                    }
                }
            }
            throw new ApplicationException("StorageCache is not set in configuration or providerType attribute does not contain proper type.");
        }

        // <moduleArea areaName="manager" providerType="BulldogCMS.Manager.ManagerProvider, BulldogCMS.Manager" />
        public IEnumerable<IModuleAreaProvider> LoadAreaModules()
        {
            List<IModuleAreaProvider> providers = new List<IModuleAreaProvider>();
            XElement areaModulesEl = _configurationDoc.Root.Element("areaModules");
            if (areaModulesEl != null)
            {
                foreach (XElement areaModuleEl in areaModulesEl.Elements())
                {
                    string providerType = areaModuleEl.GetAttributeValue<string>("providerType");
                    string areaName = areaModuleEl.GetAttributeValue<string>("areaName");
                    IModuleAreaProvider moduleAreaProvider = providerType.Instantiate<IModuleAreaProvider>();
                    moduleAreaProvider.AreaName = areaName;
                    providers.Add(moduleAreaProvider);
                }
            }
            return providers;
        }

        public IURLResolver LoadURLResolver()
        {
            XElement urlResolverEl = _configurationDoc.Root.Element("urlResolver");
            if (urlResolverEl != null)
            {
                string providerType = urlResolverEl.GetAttributeValue<string>("providerType");
                return providerType.Instantiate<IURLResolver>();
            }
            else
            {
                throw new ApplicationException("No URL resolver set in Configuration.xml");
            }
        }


    }
}